"""
    width:  -----
    height: |
            |
            |

"""
from walle_agent import  StateAgent


class Map:
    def __init__(self, map_id, width, height, nodes):
        self.map_id = map_id
        self.width = width
        self.height = height
        self.nodes = nodes
        self.nodes_map_id = {}
        self.nodes_map_xy = {}
        for node in self.nodes:
            self.nodes_map_id[node.node_id] = node
            self.nodes_map_xy[(node.x, node.y)] = node

    def get_node_by_id(self, id):
        if id in self.nodes_map_id:
            return self.nodes_map_id[id]
        else:
            return None

    def get_node_by_xy(self, x, y):
        if (x, y) in self.nodes_map_xy:
            return self.nodes_map_xy[(x, y)]
        else:
            return None


"""
    x, y: center position of grid
    neighbours: array of size 8
   0  1  2
    \ | /
  7 -  -  3
    / | \
   6  5  4  
"""
class TerraNodes:
    def __init__(self, node_id, x, y, node_type="", attribute="", neighbours=[]):
        self.node_id = node_id
        self.x = x
        self.y = y
        self.node_type = node_type
        self.attribute = attribute
        self.neighbours = neighbours

    def __str__(self):
        return ("node_id=%s, x=y%s, y=%s" % (self.node_id, self.x, self.y))

"""
    static object on map
"""
class MapObject:
    def __init__(self, object_id, current_node, z_index=0):
        self.object_id = object_id
        self.current_node = current_node
        self.x = 0
        self.y = 0
        if current_node is not None:
            self.set_current_node(current_node)
        self.z_index = z_index

    def set_current_node(self, current_node):
        self.current_node = current_node
        self.x = current_node.x
        self.y = current_node.y

"""
   x, y, z: robot's space position
   node_id: robot's current node occupation
"""
class MapAgent(StateAgent):
    def __init__(self, agent_id, current_node=None, z_index=0):
        StateAgent.__init__(self, agent_id)
        self.current_node = current_node
        self.x = 0
        self.y = 0
        if current_node is not None:
            self.set_current_node(current_node)
        self.z_index = z_index

    def set_current_node(self, current_node):
        self.current_node = current_node
        self.x = current_node.x
        self.y = current_node.y

    def __str__(self):
        return "agent_id=%, node_id=%s, x=y%s, y=%s" % (self.agent_id, self.current_node.node_id, self.x, self.y)

"""
    moving_state: stop, moving, rotating 
    moving_plan: list of TerraNodes to travel
    moving_history: list of TerraNodes has already travelled
"""
class MovingAgent(MapAgent):
    '''
        states: moving, stop
    '''
    def __init__(self, agent_id, current_node=None, z_index=0):
        MapAgent.__init__(self, agent_id, current_node, z_index)
        self.moving_plan = []
        self.moving_history = []
        self.moving_request_retry_time = 2
        self.moving_plan_request_retry_time = 100
        self.moving_target = None

    def calc_moving_time(self, next_terra):
        raise Exception("calc_moving_time is not implemented")

    def get_next_moving_terra(self):
        if len(self.moving_plan) > 0:
            return self.moving_plan.pop(0)
        else:
            return None

    def on_request_move_to_target_event(self, tick, environment, agents, event):
        if event.message is not None:
            self.moving_target = event.message["target"]
            yield self.build_event_instance(tick, tick, "request_moving_plan_event",
                                                {"agent": self, "moving_target": self.moving_target})

    def on_moving_plan_event(self, tick, environment, agents, event):
        moving_plan = event.message["moving_plan"]
        if moving_plan is None or len(moving_plan) == 0:
            yield self.build_event_instance(tick, tick + self.moving_plan_request_retry_time,
                                            "request_moving_plan_event",
                                            {"agent": self, "moving_target": self.moving_target})
        else:
            self.moving_plan = moving_plan
            next_node = self.get_next_moving_terra()
            yield self.build_event_instance(tick, tick,
                                            "request_move_to_terra_event",
                                            {"agent": self, "next_node": next_node})

    def on_moved_to_terra_event(self, tick, environment, agents, event):
        self.moving_history.append(event.message["next_node"].node_id)
        yield self.build_event_instance(tick, tick, "request_leave_terra_event",
                                        {"agent": self, "last_node": self.current_node})
        self.set_current_node(event.message["next_node"])
        if self.moving_target.node_id == self.current_node.node_id:
            yield self.build_event_instance(tick, tick, "moved_to_target_event",
                                            {"agent": self, "next_node": self.current_node})
        next_node = self.get_next_moving_terra()
        if next_node is not None:
            yield self.build_event_instance(tick, tick, "request_move_to_terra_event",
                                            {"agent": self, "next_node": next_node})

    def on_forbidden_move_to_terra_event(self, tick, environment, agents, event):
        yield self.build_event_instance(tick, tick + self.moving_request_retry_time, "request_move_to_terra_event",
                                        event.message)

    def on_approved_move_to_terra_event(self, tick, environment, agents, event):
        next_node = event.message["next_node"]
        if next_node is not None:
            moving_time = self.calc_moving_time(next_node)
            yield self.build_event_instance(tick, tick + moving_time, "moved_to_terra_event",
                                            {"agent": self, "next_node": next_node}, self.agent_id)

    def on_clear_moving_plan_event(self, tick, environment, agents, event):
        self.moving_plan = []

    def __str__(self):
        return self.agent_id


class CollisionManager(StateAgent):
    def __init__(self, agent_id):
        StateAgent.__init__(self, agent_id)

    def on_request_move_to_terra_event(self, tick, environment, agents, event):
        # to determine if collision will happen
        pass


class PathPlanner(StateAgent):
    def __init__(self, agent_id):
        StateAgent.__init__(self, agent_id)

    def on_request_leave_terra_evetn(self, tick, environment, agents, event):
        pass

    def on_request_moving_plan_event(self, tick, environment, agents, event):
        # to yield moving plan event
        pass
